package com.ssy.lingxi.settle.accounts.serviceimpl;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil;
import com.ssy.lingxi.common.constant.Constants;
import com.ssy.lingxi.common.constant.settle.ReceiptInvoiceOrderTypeEnum;
import com.ssy.lingxi.common.constant.settle.SettlementMemberOrderTypeEnum;
import com.ssy.lingxi.common.constant.settle.SettlementOrderTypeEnum;
import com.ssy.lingxi.common.constant.settle.SettlementTypeEnum;
import com.ssy.lingxi.common.exception.BusinessException;
import com.ssy.lingxi.common.model.dto.UserLoginCacheDTO;
import com.ssy.lingxi.common.response.PageData;
import com.ssy.lingxi.common.response.ResponseCode;
import com.ssy.lingxi.common.response.Wrapper;
import com.ssy.lingxi.common.utils.TokenUtil;
import com.ssy.lingxi.component.redis.service.IRedisStringUtils;
import com.ssy.lingxi.contract.api.feign.ContractControllerFeign;
import com.ssy.lingxi.contract.api.model.vo.request.PayApplyAmountVO;
import com.ssy.lingxi.dto.request.AddSettlementApplyAmountVO;
import com.ssy.lingxi.dto.request.CallbackCommunicationPayVO;
import com.ssy.lingxi.dto.request.CommunicationPayVO;
import com.ssy.lingxi.dto.request.MemberSettlementBaseVO;
import com.ssy.lingxi.message.api.constant.MessageTemplateCode;
import com.ssy.lingxi.message.api.vo.request.SystemMessageRequest;
import com.ssy.lingxi.pay.api.common.ServiceTypeEnum;
import com.ssy.lingxi.pay.api.dto.request.eAccount.EAccountOrderPayRequest;
import com.ssy.lingxi.report.api.enums.OperateDataSourceEnum;
import com.ssy.lingxi.report.api.enums.SettleAccountPayableOperateTypeEnum;
import com.ssy.lingxi.report.api.enums.SettleAccountReceivableOperateTypeEnum;
import com.ssy.lingxi.report.api.vo.request.OperateNoticeVO;
import com.ssy.lingxi.settle.accounts.entity.MemberLogisticsSettlementDO;
import com.ssy.lingxi.settle.accounts.entity.MemberOrderSettlementDO;
import com.ssy.lingxi.settle.accounts.entity.MemberProductionNoticeSettlementDO;
import com.ssy.lingxi.settle.accounts.entity.MemberSettlementDO;
import com.ssy.lingxi.settle.accounts.model.bo.PayProveBO;
import com.ssy.lingxi.settle.accounts.model.bo.SettlementOrderBO;
import com.ssy.lingxi.settle.accounts.model.constant.*;
import com.ssy.lingxi.settle.accounts.model.vo.request.*;
import com.ssy.lingxi.settle.accounts.model.vo.response.*;
import com.ssy.lingxi.settle.accounts.repository.MemberLogisticsSettlementRepository;
import com.ssy.lingxi.settle.accounts.repository.MemberOrderSettlementRepository;
import com.ssy.lingxi.settle.accounts.repository.MemberProductionNoticeSettlementRepository;
import com.ssy.lingxi.settle.accounts.repository.MemberSettlementRepository;
import com.ssy.lingxi.settle.accounts.service.IApplyAmountService;
import com.ssy.lingxi.settle.accounts.service.IFeignService;
import com.ssy.lingxi.settle.accounts.service.IMemberSettlementService;
import com.ssy.lingxi.settle.accounts.utils.SerialNumberUtils;
import org.apache.commons.lang.RandomStringUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.persistence.criteria.Predicate;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 会员结算接口实现
 *
 * @author 伍将
 * @version 2.0.0
 * @date 2020/10/23
 */
@Service
public class MemberSettlementServiceImpl implements IMemberSettlementService {

    @Resource
    private MemberSettlementRepository repository;

    @Resource
    private MemberLogisticsSettlementRepository logisticsRepository;

    @Resource
    private MemberProductionNoticeSettlementRepository productionNoticeRepository;

    @Resource
    private MemberOrderSettlementRepository orderRepository;

    @Resource
    private IApplyAmountService applyAmountService;

    @Resource
    private IFeignService feignService;

    @Resource
    private IRedisStringUtils redisStringUtils;

    @Resource
    private ContractControllerFeign controllerFeign;


    /**
     * 分页查询应付账款结算
     *
     * @param sysUser:
     * @param pageVO:
     * @return com.ssy.lingxi.common.response.Wrapper<com.ssy.lingxi.common.response.PageData < com.ssy.lingxi.settle.accounts.mode.vo.response.MemberSettlementVO>>
     * @author 伍将
     * @date 2020/10/23
     **/
    @Override
    public Wrapper<PageData<MemberSettlementVO>> pagePayableSettlement(UserLoginCacheDTO sysUser, MemberSettlementPageVO pageVO) {
        return pageSettlement(sysUser, pageVO, false);
    }

    /**
     * 分页查询会员结算
     *
     * @param sysUser:
     * @param pageVO:
     * @return com.ssy.lingxi.common.response.Wrapper<com.ssy.lingxi.common.response.PageData < com.ssy.lingxi.settle.accounts.mode.vo.response.MemberSettlementVO>>
     * @author 伍将
     * @date 2020/10/23
     **/
    private Wrapper<PageData<MemberSettlementVO>> pageSettlement(UserLoginCacheDTO sysUser, MemberSettlementPageVO pageVO, Boolean isSettlement) {
        // step 1: 组装查询条件
        Specification<MemberSettlementDO> spec = (root, query, criteriaBuilder) -> {
            List<Predicate> list = new ArrayList<>();
            if (!pageVO.getStatus().equals(SettlementStatusEnum.ALL.getCode())) {
                list.add(criteriaBuilder.equal(root.get("status").as(Integer.class), pageVO.getStatus()));
            }
            if (!pageVO.getOrderType().equals(SettlementStatusEnum.ALL.getCode())) {
                list.add(criteriaBuilder.equal(root.get("orderType").as(Integer.class), pageVO.getOrderType()));
            }
            if (StringUtils.hasLength(pageVO.getStartTime())) {
                list.add(criteriaBuilder.greaterThanOrEqualTo(root.get("settlementDate").as(Long.class), DateUtil.parse(pageVO.getStartTime()).getTime()));
            }
            if (StringUtils.hasLength(pageVO.getEndTime())) {
                list.add(criteriaBuilder.lessThanOrEqualTo(root.get("settlementDate").as(Long.class), DateUtil.parse(pageVO.getEndTime()).getTime()));
            }
            //预付时间
            if (pageVO.getPrePayStartTime()!=null) {
                list.add(criteriaBuilder.greaterThanOrEqualTo(root.get("prePayTime").as(Long.class), pageVO.getPrePayStartTime()));
            }
            if (pageVO.getPrePayEndTime()!=null) {
                list.add(criteriaBuilder.lessThanOrEqualTo(root.get("prePayTime").as(Long.class), pageVO.getPrePayEndTime()));
            }
            //实付时间
            if (pageVO.getPayStartTime()!=null) {
                list.add(criteriaBuilder.greaterThanOrEqualTo(root.get("payTime").as(Long.class), pageVO.getPayStartTime()));
            }
            if (pageVO.getPayEndTime()!=null) {
                list.add(criteriaBuilder.lessThanOrEqualTo(root.get("payTime").as(Long.class), pageVO.getPayEndTime()));
            }
            if (!isSettlement) {
                list.add(criteriaBuilder.equal(root.get("payMemberId").as(Long.class), sysUser.getMemberId()));
                list.add(criteriaBuilder.equal(root.get("payRoleId").as(Long.class), sysUser.getMemberRoleId()));
                if (StringUtils.hasLength(pageVO.getSettlementName())) {
                    list.add(criteriaBuilder.like(root.get("settlementName").as(String.class), "%" + pageVO.getSettlementName().trim() + "%"));
                }
            } else {
                list.add(criteriaBuilder.equal(root.get("memberId").as(Long.class), sysUser.getMemberId()));
                list.add(criteriaBuilder.equal(root.get("roleId").as(Long.class), sysUser.getMemberRoleId()));
                if (StringUtils.hasLength(pageVO.getPayName())) {
                    list.add(criteriaBuilder.like(root.get("payName").as(String.class), "%" + pageVO.getPayName().trim() + "%"));
                }
            }
            if (StringUtils.hasLength(pageVO.getSettlementNo())) {
                list.add(criteriaBuilder.like(root.get("settlementNo").as(String.class), "%" + pageVO.getSettlementNo().trim() + "%"));
            }
            Predicate[] p = new Predicate[list.size()];
            return criteriaBuilder.and(list.toArray(p));
        };

        // step 2: 组装分页参数
        Pageable page = PageRequest.of(pageVO.getCurrent() - 1, pageVO.getPageSize(), Sort.by("id").descending());

        // step 3: 分页查询数据
        Page<MemberSettlementDO> result = repository.findAll(spec, page);

        // step 4: 组装返回数据
        List<MemberSettlementVO> resultList = result.getContent().stream().map(r -> {
            MemberSettlementVO queryVO = new MemberSettlementVO();
            queryVO.setId(r.getId());
            queryVO.setSettlementNo(r.getSettlementNo());
            queryVO.setSettlementDate(DateUtil.format(DateUtil.date(r.getSettlementDate()), "yyyy-MM-dd"));
            queryVO.setSettlementWay(r.getSettlementWay());
            queryVO.setSettlementWayName(SettlementWayEnum.getMessage(r.getSettlementWay()));
            queryVO.setSettlementName(r.getSettlementName());
            queryVO.setPayName(r.getPayName());
            queryVO.setOrderType(r.getOrderType());
            queryVO.setOrderTypeName(SettlementOrderTypeEnum.getMessage(r.getOrderType(), (null != r.getOrderBO() ? r.getOrderBO().getOrderNo() : "")));
            queryVO.setOrderNo(Optional.ofNullable(r.getOrderBO()).map(SettlementOrderBO::getOrderNo).orElse(""));
            queryVO.setTotalCount(r.getTotalCount());
            queryVO.setAmount(BigDecimal.valueOf(r.getAmount()).setScale(2, RoundingMode.HALF_UP).doubleValue());
            queryVO.setSettlementTime(DateUtil.format(DateUtil.date(r.getSettlementTime()), "yyyy-MM-dd HH:mm"));
            queryVO.setStatus(r.getStatus());
            queryVO.setStatusName(SettlementStatusEnum.getMessage(r.getStatus()));
            queryVO.setPayWay(r.getPayWay());
            queryVO.setPayWayName(SettlementTypeEnum.getName(r.getPayWay())); // 支付方式
            queryVO.setMemberId(r.getMemberId());
            queryVO.setRoleId(r.getRoleId());
            queryVO.setPrePayTime(DateUtil.format(DateUtil.date(r.getPrePayTime()), "yyyy-MM-dd"));
            queryVO.setPayTime((null != r.getPayTime() && r.getPayTime() > 0) ? DateUtil.format(DateUtil.date(r.getPayTime()), "yyyy-MM-dd") : "");
            queryVO.setOrderId(null != r.getOrderBO() ? r.getOrderBO().getOrderId() : 0);
            return queryVO;
        }).collect(Collectors.toList());

        return Wrapper.success(new PageData<>(result.getTotalElements(), resultList));
    }

    /**
     * 获取应付账款结算详情
     *
     * @param sysUser:
     * @param id:
     * @return com.ssy.lingxi.common.response.Wrapper<com.ssy.lingxi.settle.accounts.mode.vo.response.MemberSettlementDetailVO>
     * @author 伍将
     * @date 2020/10/23
     **/
    @Override
    public Wrapper<MemberSettlementDetailVO> getPayableDetail(UserLoginCacheDTO sysUser, Long id) {
        return getDetail(sysUser, id);
    }

    /**
     * 获取会员结算详情
     *
     * @param sysUser:
     * @param id:
     * @return com.ssy.lingxi.common.response.Wrapper<com.ssy.lingxi.settle.accounts.mode.vo.response.MemberSettlementDetailVO>
     * @author 伍将
     * @date 2020/10/23
     **/
    private Wrapper<MemberSettlementDetailVO> getDetail(UserLoginCacheDTO sysUser, Long id) {
        MemberSettlementDO entity = repository.findById(id).orElse(null);
        if (entity == null) {
            throw new BusinessException(ResponseCode.SA_MEMBER_SETTLEMENT_NOT_EXIST);
        }

        MemberSettlementDetailVO result = new MemberSettlementDetailVO();
        result.setId(entity.getId());
        result.setSettlementNo(entity.getSettlementNo());
        result.setSettlementWayName(SettlementWayEnum.getMessage(entity.getSettlementWay()));
        result.setSettlementName(entity.getSettlementName());
        result.setPayName(entity.getPayName());
        result.setOrderTypeName(SettlementOrderTypeEnum.getMessage(entity.getOrderType(), (null != entity.getOrderBO() ? entity.getOrderBO().getOrderNo() : "")));
        result.setTotalCount(entity.getTotalCount());
        result.setAmount(BigDecimal.valueOf(entity.getAmount()).setScale(2, RoundingMode.HALF_UP).doubleValue());
        result.setSettlementDate(DateUtil.format(DateUtil.date(entity.getSettlementDate()), "yyyy-MM-dd"));
        result.setStatusName(SettlementStatusEnum.getMessage(entity.getStatus()));

        return Wrapper.success(result);
    }

    /**
     * 构建操作者（用于内部流转记录）
     *
     * @param sysUser 登录会员
     * @return 构建操作者（用于内部流转记录）
     */
    private UserLoginCacheDTO buildOperator(UserLoginCacheDTO sysUser) {
        UserLoginCacheDTO operator = new UserLoginCacheDTO();
        operator.setUserId(sysUser.getUserId());
        operator.setMemberId(sysUser.getMemberId());
        operator.setMemberRoleId(sysUser.getMemberRoleId());
        operator.setUserName(sysUser.getUserName());
        operator.setUserOrgName(sysUser.getUserOrgName());
        operator.setUserJobTitle(sysUser.getUserJobTitle());
        return operator;
    }

    /**
     * 付款
     *
     * @param sysUser:
     * @param payVO:
     * @return com.ssy.lingxi.common.response.Wrapper
     * @author 伍将
     * @date 2020/10/23
     **/
    @Override
    @Transactional
    public Wrapper<Void> pay(UserLoginCacheDTO sysUser, PayVO payVO) {
        // step1: 校验
        MemberSettlementDO entity = repository.findById(payVO.getId()).orElse(null);
        if (entity == null) {
            throw new BusinessException(ResponseCode.SA_MEMBER_SETTLEMENT_NOT_EXIST);
        }
        if (!entity.getStatus().equals(SettlementStatusEnum.TO_BE_PAY.getCode())) {
            throw new BusinessException(ResponseCode.SA_MEMBER_SETTLEMENT_NO_PAY);
        }
        // step2: 更新应收账款信息
        Long now = System.currentTimeMillis();
        entity.setPayProveList(payVO.getProveList());
        entity.setStatus(SettlementStatusEnum.TO_BE_RECEIPT.getCode());
        entity.setUpdateTime(now);
        entity.setPayTime(now);
        repository.saveAndFlush(entity);
        // step5: 根据业务不同处理
        // 如果是请款单，则调用合同内部结果更新请款状态
        if (SettlementOrderTypeEnum.APPLY_AMOUNT.getCode().equals(entity.getOrderType())) {
            PayApplyAmountVO payApplyAmountVO = new PayApplyAmountVO();
            payApplyAmountVO.setApplyId(entity.getOrderBO().getOrderId());
            payApplyAmountVO.setUser(buildOperator(sysUser));
            controllerFeign.payApplyAmount(payApplyAmountVO);
            // 请款单完成付款
            SettlementOrderBO billData = entity.getOrderBO();
            applyAmountService.pay(billData.getOrderId());
        }
        // step3: 消息通知
        // 新增操作通知
        List<OperateNoticeVO> noticeVOList = new ArrayList<>();
        // 支付方待付款-1，待收款+1
        OperateNoticeVO noticeVO = new OperateNoticeVO();
        noticeVO.setMemberId(entity.getPayMemberId());
        noticeVO.setRoleId(entity.getPayRoleId());
        noticeVO.setDataSource(OperateDataSourceEnum.SETTLE_ACCOUNT_PAYABLE.getCode());
        noticeVO.setReduceOperateType(SettleAccountPayableOperateTypeEnum.TO_BE_PAY.getCode());
        noticeVO.setReduceCount(1L);
        noticeVO.setIncreaseOperateType(SettleAccountPayableOperateTypeEnum.TO_BE_RECEIPT.getCode());
        noticeVO.setIncreaseCount(1L);
        noticeVOList.add(noticeVO);
        // 收款方待付款-1，待收款+1
        noticeVO = new OperateNoticeVO();
        noticeVO.setMemberId(entity.getMemberId());
        noticeVO.setRoleId(entity.getRoleId());
        noticeVO.setDataSource(OperateDataSourceEnum.SETTLE_ACCOUNT_RECEIVABLE.getCode());
        noticeVO.setReduceOperateType(SettleAccountReceivableOperateTypeEnum.TO_BE_PAY.getCode());
        noticeVO.setReduceCount(1L);
        noticeVO.setIncreaseOperateType(SettleAccountReceivableOperateTypeEnum.TO_BE_RECEIPT.getCode());
        noticeVO.setIncreaseCount(1L);
        noticeVOList.add(noticeVO);
        feignService.addSystemOperateNoticeList(noticeVOList);

        // 新增系统通知
        SystemMessageRequest request = new SystemMessageRequest();
        request.setMemberId(entity.getMemberId());
        request.setRoleId(entity.getRoleId());
        request.setMessageNotice(MessageTemplateCode.receipt_settle_gathering);
        List<String> params = new ArrayList<>();
        params.add(entity.getSettlementNo());
        request.setParams(params);
        feignService.sendSystemMessage(request);


        return Wrapper.success();
    }

    /**
     * 付款方查看凭证
     *
     * @param sysUser:
     * @param id:
     * @return com.ssy.lingxi.common.response.Wrapper<java.util.List < com.ssy.lingxi.settle.accounts.mode.bo.PayProveBO>>
     * @author 伍将
     * @date 2020/10/23
     **/
    @Override
    public Wrapper<List<PayProveBO>> getPayablePayProve(UserLoginCacheDTO sysUser, Long id) {
        return getPayProve(sysUser, id);
    }

    /**
     * 获取支付凭证
     *
     * @param sysUser:
     * @param id:
     * @return com.ssy.lingxi.common.response.Wrapper<java.util.List < com.ssy.lingxi.settle.accounts.mode.bo.PayProveBO>>
     * @author 伍将
     * @date 2020/10/23
     **/
    private Wrapper<List<PayProveBO>> getPayProve(UserLoginCacheDTO sysUser, Long id) {

        MemberSettlementDO entity = repository.findById(id).orElse(null);
        if (entity == null) {
            throw new BusinessException(ResponseCode.SA_MEMBER_SETTLEMENT_NOT_EXIST);
        }

        return Wrapper.success(entity.getPayProveList());
    }

    /**
     * 分页查询应收账款结算
     *
     * @param sysUser:
     * @param pageVO:
     * @return com.ssy.lingxi.common.response.Wrapper<com.ssy.lingxi.common.response.PageData < com.ssy.lingxi.settle.accounts.mode.vo.response.MemberSettlementVO>>
     * @author 伍将
     * @date 2020/10/23
     **/
    @Override
    public Wrapper<PageData<MemberSettlementVO>> pageReceivableSettlement(UserLoginCacheDTO sysUser, MemberSettlementPageVO pageVO) {
        return pageSettlement(sysUser, pageVO, true);
    }

    /**
     * 获取应收款账款结算详情
     *
     * @param sysUser:
     * @param id:
     * @return com.ssy.lingxi.common.response.Wrapper<com.ssy.lingxi.settle.accounts.mode.vo.response.MemberSettlementDetailVO>
     * @author 伍将
     * @date 2020/10/23
     **/
    @Override
    public Wrapper<MemberSettlementDetailVO> getReceivableDetail(UserLoginCacheDTO sysUser, Long id) {
        return getDetail(sysUser, id);
    }

    /**
     * 确认对账完成
     *
     * @param sysUser:
     * @param completeVO:
     * @return com.ssy.lingxi.common.response.Wrapper
     * @author 伍将
     * @date 2020/10/23
     **/
    @Override
    public Wrapper<Void> confirmAccountComplete(UserLoginCacheDTO sysUser, ConfirmAccountCompleteVO completeVO) {
        MemberSettlementDO entity = repository.findById(completeVO.getSettlementId()).orElse(null);
        if (entity == null) {
            throw new BusinessException(ResponseCode.SA_MEMBER_SETTLEMENT_NOT_EXIST);
        }
        if (!entity.getStatus().equals(SettlementStatusEnum.TO_BE_ACCOUNT_COMPLETE.getCode())) {
            throw new BusinessException(ResponseCode.SA_MEMBER_SETTLEMENT_NO_CONFIRM_ACCOUNT_COMPLETE);
        }
        entity.setStatus(SettlementStatusEnum.TO_BE_PAY.getCode());
        entity.setUpdateTime(System.currentTimeMillis());
        repository.saveAndFlush(entity);

/* report定时任务已生成       //如果为请款单，生成开票记录
        if(SettlementOrderTypeEnum.APPLY_AMOUNT.getCode().equals(entity.getOrderType())){
            MemberReceiptInvoiceDO invoiceDO=new MemberReceiptInvoiceDO();
            invoiceDO.setInvoiceStatus(ReceiptInvoiceStatusEnum.NO.getCode());
            invoiceDO.setMemberId(entity.getMemberId());
            invoiceDO.setRoleId(entity.getRoleId());
            invoiceDO.setPayMemberId(entity.getPayMemberId());
            invoiceDO.setPayRoleId(entity.getPayRoleId());
            invoiceDO.setOrderNo(entity.getSettlementNo());
            invoiceDO.setOrderAbstract("请款单结算");
            invoiceDO.setOrderType(entity.getOrderType());
            invoiceDO.setOrderTime(entity.getSettlementTime());
            invoiceDO.setOrderAmount(entity.getAmount());
            invoiceDO.setBatch(1);
            invoiceDO.setPayAmount(entity.getAmount());
            invoiceDO.setPayStatus(ReceiptInvoicePayStatusEnum.NO.getCode());
            invoiceDO.setPayNode("结算支付");
            invoiceDO.setDataId(entity.getId());
            invoiceRepository.saveAndFlush(invoiceDO);
        }*/

        // 新增操作通知
        List<OperateNoticeVO> noticeVOList = new ArrayList<>();
        // 支付方待对账-1，待付款+1
        OperateNoticeVO noticeVO = new OperateNoticeVO();
        noticeVO.setMemberId(entity.getPayMemberId());
        noticeVO.setRoleId(entity.getPayRoleId());
        noticeVO.setDataSource(OperateDataSourceEnum.SETTLE_ACCOUNT_PAYABLE.getCode());
        noticeVO.setReduceOperateType(SettleAccountPayableOperateTypeEnum.TO_BE_ACCOUNTS.getCode());
        noticeVO.setReduceCount(1L);
        noticeVO.setIncreaseOperateType(SettleAccountPayableOperateTypeEnum.TO_BE_PAY.getCode());
        noticeVO.setIncreaseCount(1L);
        noticeVOList.add(noticeVO);
        // 收款方待对账-1，待付款+1
        noticeVO = new OperateNoticeVO();
        noticeVO.setMemberId(entity.getMemberId());
        noticeVO.setRoleId(entity.getRoleId());
        noticeVO.setDataSource(OperateDataSourceEnum.SETTLE_ACCOUNT_RECEIVABLE.getCode());
        noticeVO.setReduceOperateType(SettleAccountReceivableOperateTypeEnum.TO_BE_ACCOUNTS.getCode());
        noticeVO.setReduceCount(1L);
        noticeVO.setIncreaseOperateType(SettleAccountReceivableOperateTypeEnum.TO_BE_PAY.getCode());
        noticeVO.setIncreaseCount(1L);
        noticeVOList.add(noticeVO);
        feignService.addSystemOperateNoticeList(noticeVOList);

        // 新增系统通知
        SystemMessageRequest request = new SystemMessageRequest();
        request.setMemberId(entity.getPayMemberId());
        request.setRoleId(entity.getPayRoleId());
        request.setMessageNotice(MessageTemplateCode.payment_settle_payment);
        List<String> params = new ArrayList<>();
        params.add(entity.getSettlementNo());
        request.setParams(params);
        feignService.sendSystemMessage(request);

        return Wrapper.success();
    }

    /**
     * 确认付款凭证
     *
     * @param sysUser:
     * @param confirmVO:
     * @return com.ssy.lingxi.common.response.Wrapper
     * @author 伍将
     * @date 2020/10/23
     **/
    @Override
    public Wrapper<Void> confirmPayProve(UserLoginCacheDTO sysUser, ConfirmPayProve confirmVO) {
        MemberSettlementDO entity = repository.findById(confirmVO.getId()).orElse(null);
        if (entity == null) {
            throw new BusinessException(ResponseCode.SA_MEMBER_SETTLEMENT_NOT_EXIST);
        }
        if (!entity.getStatus().equals(SettlementStatusEnum.TO_BE_RECEIPT.getCode())) {
            throw new BusinessException(ResponseCode.SA_MEMBER_SETTLEMENT_NO_CONFIRM_PAY_PROVE);
        }
        if (confirmVO.getStatus().equals(CommonBooleanEnum.YES.getCode())) {
            entity.setStatus(SettlementStatusEnum.COMPLETE.getCode());
        } else {
            entity.setStatus(SettlementStatusEnum.TO_BE_PAY.getCode());
        }
        entity.setUpdateTime(System.currentTimeMillis());
        repository.saveAndFlush(entity);

        // 新增操作通知
        List<OperateNoticeVO> noticeVOList = new ArrayList<>();
        // 支付方待收款-1，已完成/待付款+1
        OperateNoticeVO noticeVO = new OperateNoticeVO();
        noticeVO.setMemberId(entity.getPayMemberId());
        noticeVO.setRoleId(entity.getPayRoleId());
        noticeVO.setDataSource(OperateDataSourceEnum.SETTLE_ACCOUNT_PAYABLE.getCode());
        noticeVO.setReduceOperateType(SettleAccountPayableOperateTypeEnum.TO_BE_RECEIPT.getCode());
        noticeVO.setReduceCount(1L);
        if (SettlementStatusEnum.COMPLETE.getCode().equals(entity.getStatus())) {
            noticeVO.setIncreaseOperateType(SettleAccountPayableOperateTypeEnum.COMPLETE.getCode());
        } else if (SettlementStatusEnum.TO_BE_PAY.getCode().equals(entity.getStatus())) {
            noticeVO.setIncreaseOperateType(SettleAccountPayableOperateTypeEnum.TO_BE_PAY.getCode());
        }
        noticeVO.setIncreaseCount(1L);
        noticeVOList.add(noticeVO);
        // 收款方待收款-1，已完成/待付款+1
        noticeVO = new OperateNoticeVO();
        noticeVO.setMemberId(entity.getMemberId());
        noticeVO.setRoleId(entity.getRoleId());
        noticeVO.setDataSource(OperateDataSourceEnum.SETTLE_ACCOUNT_RECEIVABLE.getCode());
        noticeVO.setReduceOperateType(SettleAccountPayableOperateTypeEnum.TO_BE_RECEIPT.getCode());
        noticeVO.setReduceCount(1L);
        if (SettlementStatusEnum.COMPLETE.getCode().equals(entity.getStatus())) {
            noticeVO.setIncreaseOperateType(SettleAccountReceivableOperateTypeEnum.COMPLETE.getCode());
        } else if (SettlementStatusEnum.TO_BE_PAY.getCode().equals(entity.getStatus())) {
            noticeVO.setIncreaseOperateType(SettleAccountReceivableOperateTypeEnum.TO_BE_PAY.getCode());
        }
        noticeVO.setIncreaseCount(1L);
        noticeVOList.add(noticeVO);
        feignService.addSystemOperateNoticeList(noticeVOList);

        if (SettlementStatusEnum.TO_BE_PAY.getCode().equals(entity.getStatus())) {
            // 新增系统通知
            SystemMessageRequest request = new SystemMessageRequest();
            request.setMemberId(entity.getPayMemberId());
            request.setRoleId(entity.getPayRoleId());
            request.setMessageNotice(MessageTemplateCode.payment_settle_payment);
            List<String> params = new ArrayList<>();
            params.add(entity.getSettlementNo());
            request.setParams(params);
            feignService.sendSystemMessage(request);
        }

        return Wrapper.success();
    }

    /**
     * 收款方查看凭证
     *
     * @param sysUser:
     * @param id:
     * @return com.ssy.lingxi.common.response.Wrapper<java.util.List < com.ssy.lingxi.settle.accounts.mode.bo.PayProveBO>>
     * @author 伍将
     * @date 2020/10/23
     **/
    @Override
    public Wrapper<List<PayProveBO>> getReceivablePayProve(UserLoginCacheDTO sysUser, Long id) {
        return getPayProve(sysUser, id);
    }

    /**
     * 分页查询应付物流结算
     *
     * @param sysUser:
     * @param pageVO:
     * @return com.ssy.lingxi.common.response.Wrapper<com.ssy.lingxi.common.response.PageData < com.ssy.lingxi.settle.accounts.mode.vo.response.MemberLogisticsSettlementVO>>
     * @author 伍将
     * @date 2020/10/26
     **/
    @Override
    public Wrapper<PageData<MemberLogisticsSettlementVO>> pagePayableLogisticsSettlement(UserLoginCacheDTO sysUser, MemberLogisticsSettlementPageVO pageVO) {
        return pageLogisticsSettlement(sysUser, pageVO);
    }

    /**
     * 分页查询应收物流结算
     *
     * @param sysUser:
     * @param pageVO:
     * @return com.ssy.lingxi.common.response.Wrapper<com.ssy.lingxi.common.response.PageData < com.ssy.lingxi.settle.accounts.mode.vo.response.MemberLogisticsSettlementVO>>
     * @author 伍将
     * @date 2020/10/26
     **/
    @Override
    public Wrapper<PageData<MemberLogisticsSettlementVO>> pageReceivableLogisticsSettlement(UserLoginCacheDTO sysUser, MemberLogisticsSettlementPageVO pageVO) {
        return pageLogisticsSettlement(sysUser, pageVO);
    }

    /**
     * 分页查询物流结算
     *
     * @param sysUser:
     * @param pageVO:
     * @return com.ssy.lingxi.common.response.Wrapper<com.ssy.lingxi.common.response.PageData < com.ssy.lingxi.settle.accounts.mode.vo.response.MemberLogisticsSettlementVO>>
     * @author 伍将
     * @date 2020/10/26
     **/
    private Wrapper<PageData<MemberLogisticsSettlementVO>> pageLogisticsSettlement(UserLoginCacheDTO sysUser, MemberLogisticsSettlementPageVO pageVO) {
        // step 1: 组装查询条件
        Specification<MemberLogisticsSettlementDO> spec = (root, query, criteriaBuilder) -> {
            List<Predicate> list = new ArrayList<>();
            list.add(criteriaBuilder.equal(root.get("settlementId").as(Long.class), pageVO.getSettlementId()));

            if (StringUtils.hasLength(pageVO.getStartTime())) {
                list.add(criteriaBuilder.greaterThanOrEqualTo(root.get("orderTime").as(Long.class), DateUtil.parse(pageVO.getStartTime()).getTime()));
            }
            if (StringUtils.hasLength(pageVO.getEndTime())) {
                list.add(criteriaBuilder.lessThanOrEqualTo(root.get("orderTime").as(Long.class), DateUtil.parse(pageVO.getEndTime()).getTime()));
            }
            if (StringUtils.hasLength(pageVO.getOrderNo())) {
                list.add(criteriaBuilder.like(root.get("orderNo").as(String.class), "%" + pageVO.getOrderNo().trim() + "%"));
            }
            if (StringUtils.hasLength(pageVO.getOrderAbstract())) {
                list.add(criteriaBuilder.like(root.get("orderAbstract").as(String.class), "%" + pageVO.getOrderAbstract().trim() + "%"));
            }
            Predicate[] p = new Predicate[list.size()];
            return criteriaBuilder.and(list.toArray(p));
        };

        // step 2: 组装分页参数
        Pageable page = PageRequest.of(pageVO.getCurrent() - 1, pageVO.getPageSize(), Sort.by("orderTime").ascending());

        // step 3: 分页查询数据
        Page<MemberLogisticsSettlementDO> result = logisticsRepository.findAll(spec, page);

        // step 4: 组装返回数据
        List<MemberLogisticsSettlementVO> resultList = result.getContent().stream().map(r -> {
            MemberLogisticsSettlementVO queryVO = new MemberLogisticsSettlementVO();
            queryVO.setOrderNo(r.getOrderNo());
            queryVO.setOrderAbstract(r.getOrderAbstract());
            queryVO.setOrderTypeName(SettlementOrderTypeEnum.getMessage(SettlementOrderTypeEnum.LOGISTICS.getCode(), ""));
            queryVO.setOrderTime(DateUtil.format(DateUtil.date(r.getOrderTime()), "yyyy-MM-dd HH:mm"));
            queryVO.setTotalCarton(BigDecimal.valueOf(r.getTotalCarton()).setScale(2, RoundingMode.HALF_UP).doubleValue());
            queryVO.setTotalVolume(BigDecimal.valueOf(r.getTotalVolume()).setScale(2, RoundingMode.HALF_UP).doubleValue());
            queryVO.setTotalWeight(BigDecimal.valueOf(r.getTotalWeight()).setScale(2, RoundingMode.HALF_UP).doubleValue());
            queryVO.setOrderAmount(BigDecimal.valueOf(r.getOrderAmount()).setScale(2, RoundingMode.HALF_UP).doubleValue());
            queryVO.setAcceptOrderTime(DateUtil.format(DateUtil.date(r.getAcceptOrderTime()), "yyyy-MM-dd HH:mm"));
            queryVO.setSettlementAmount(BigDecimal.valueOf(r.getSettlementAmount()).setScale(2, RoundingMode.HALF_UP).doubleValue());
            queryVO.setIsHasTax(r.getIsHasTax());
            queryVO.setIsHasTaxName(CommonBooleanEnum.getMessage(r.getIsHasTax()));
            queryVO.setTaxRate(r.getTaxRate());
            return queryVO;
        }).collect(Collectors.toList());

        return Wrapper.success(new PageData<>(result.getTotalElements(), resultList));
    }

    /**
     * 分页查询应付生产通知单结算
     *
     * @param sysUser:
     * @param pageVO:
     * @return com.ssy.lingxi.common.response.Wrapper<com.ssy.lingxi.common.response.PageData < com.ssy.lingxi.settle.accounts.mode.vo.response.MemberProductionNoticeSettlementVO>>
     * @author 伍将
     * @date 2020/10/26
     **/
    @Override
    public Wrapper<PageData<MemberProductionNoticeSettlementVO>> pagePayableProductionNoticeSettlement(UserLoginCacheDTO sysUser, MemberProductionNoticeSettlementPageVO pageVO) {
        return pageProductionNoticeSettlement(sysUser, pageVO);
    }

    /**
     * 分页查询应收生产通知单结算
     *
     * @param sysUser:
     * @param pageVO:
     * @return com.ssy.lingxi.common.response.Wrapper<com.ssy.lingxi.common.response.PageData < com.ssy.lingxi.settle.accounts.mode.vo.response.MemberProductionNoticeSettlementVO>>
     * @author 伍将
     * @date 2020/10/26
     **/
    @Override
    public Wrapper<PageData<MemberProductionNoticeSettlementVO>> pageReceivableProductionNoticeSettlement(UserLoginCacheDTO sysUser, MemberProductionNoticeSettlementPageVO pageVO) {
        return pageProductionNoticeSettlement(sysUser, pageVO);
    }

    private Wrapper<PageData<MemberProductionNoticeSettlementVO>> pageProductionNoticeSettlement(UserLoginCacheDTO sysUser, MemberProductionNoticeSettlementPageVO pageVO) {
        // step 1: 组装查询条件
        Specification<MemberProductionNoticeSettlementDO> spec = (root, query, criteriaBuilder) -> {
            List<Predicate> list = new ArrayList<>();
            list.add(criteriaBuilder.equal(root.get("settlementId").as(Long.class), pageVO.getSettlementId()));

            if (StringUtils.hasLength(pageVO.getStartTime())) {
                list.add(criteriaBuilder.greaterThanOrEqualTo(root.get("orderTime").as(Long.class), DateUtil.parse(pageVO.getStartTime()).getTime()));
            }
            if (StringUtils.hasLength(pageVO.getEndTime())) {
                list.add(criteriaBuilder.lessThanOrEqualTo(root.get("orderTime").as(Long.class), DateUtil.parse(pageVO.getEndTime()).getTime()));
            }
            if (StringUtils.hasLength(pageVO.getReceiveStartTime())) {
                list.add(criteriaBuilder.greaterThanOrEqualTo(root.get("receiveTime").as(Long.class), DateUtil.parse(pageVO.getReceiveStartTime()).getTime()));
            }
            if (StringUtils.hasLength(pageVO.getReceiveEndTime())) {
                list.add(criteriaBuilder.lessThanOrEqualTo(root.get("receiveTime").as(Long.class), DateUtil.parse(pageVO.getReceiveEndTime()).getTime()));
            }
            if (StringUtils.hasLength(pageVO.getOrderNo())) {
                list.add(criteriaBuilder.like(root.get("orderNo").as(String.class), "%" + pageVO.getOrderNo().trim() + "%"));
            }
            if (StringUtils.hasLength(pageVO.getOrderAbstract())) {
                list.add(criteriaBuilder.like(root.get("orderAbstract").as(String.class), "%" + pageVO.getOrderAbstract().trim() + "%"));
            }
            Predicate[] p = new Predicate[list.size()];
            return criteriaBuilder.and(list.toArray(p));
        };

        // step 2: 组装分页参数
        Pageable page = PageRequest.of(pageVO.getCurrent() - 1, pageVO.getPageSize(), Sort.by("orderTime").ascending());

        // step 3: 分页查询数据
        Page<MemberProductionNoticeSettlementDO> result = productionNoticeRepository.findAll(spec, page);

        // step 4: 组装返回数据
        List<MemberProductionNoticeSettlementVO> resultList = result.getContent().stream().map(r -> {
            MemberProductionNoticeSettlementVO queryVO = new MemberProductionNoticeSettlementVO();
            queryVO.setOrderNo(r.getOrderNo());
            queryVO.setOrderAbstract(r.getOrderAbstract());
            queryVO.setOrderTypeName(SettlementOrderTypeEnum.getMessage(SettlementOrderTypeEnum.PRODUCTION_NOTICE.getCode(), ""));
            queryVO.setOrderTime(DateUtil.format(DateUtil.date(r.getOrderTime()), "yyyy-MM-dd HH:mm"));
            queryVO.setOrderAmount(BigDecimal.valueOf(r.getOrderAmount()).setScale(2, RoundingMode.HALF_UP).doubleValue());
            queryVO.setBatch(r.getBatch());
            queryVO.setReceiveTime(DateUtil.format(DateUtil.date(r.getReceiveTime()), "yyyy-MM-dd HH:mm"));
            queryVO.setReceiveCount(r.getReceiveCount());
            queryVO.setSettlementAmount(BigDecimal.valueOf(r.getSettlementAmount()).setScale(2, RoundingMode.HALF_UP).doubleValue());
            queryVO.setIsHasTax(r.getIsHasTax());
            queryVO.setIsHasTaxName(CommonBooleanEnum.getMessage(r.getIsHasTax()));
            queryVO.setTaxRate(r.getTaxRate());
            return queryVO;
        }).collect(Collectors.toList());

        return Wrapper.success(new PageData<>(result.getTotalElements(), resultList));
    }

    /**
     * 新增请款单
     *
     * @param addVO: 新增请求参数
     * @return 操作结果
     * @author 伍将
     * @date 2021/3/24
     **/
    @Override
    public Wrapper<Void> addApplyAmount(AddSettlementApplyAmountVO addVO) {

        MemberSettlementDO entity = new MemberSettlementDO();
        long now = System.currentTimeMillis();
        entity.setMemberId(addVO.getMemberId());
        entity.setRoleId(addVO.getRoleId());
        entity.setSettlementName(addVO.getSettlementName());
        entity.setPayMemberId(addVO.getPayMemberId());
        entity.setPayRoleId(addVO.getPayRoleId());
        entity.setPayName(addVO.getPayName());
        String settlementNo = SerialNumberUtils.getNumber(redisStringUtils, Constants.SETTLEMENT_CODE, Constants.SETTLEMENT_CODE_DATE, Constants.SETTLEMENT_CODE_NUM_LEN);
        entity.setSettlementNo(settlementNo);
        entity.setSettlementWay(addVO.getSettlementWay());
        entity.setOrderType(SettlementOrderTypeEnum.APPLY_AMOUNT.getCode());
        entity.setStartTime(0L);
        entity.setEndTime(0L);
        entity.setTotalCount(addVO.getTotalCount());
        entity.setAmount(addVO.getAmount());
        entity.setSettlementDate(now);
        entity.setSettlementTime(now);
        entity.setPrePayTime(addVO.getPrePayTime());
        entity.setPayTime(0L);
        entity.setStatus(SettlementStatusEnum.TO_BE_ACCOUNT_COMPLETE.getCode());
        entity.setPayWay(PayWayEnum.OFFLINE_PAYMENT.getCode());
        entity.setStrategyId(0L);
        entity.setUpdateTime(now);
        SettlementOrderBO orderBO = new SettlementOrderBO();
        orderBO.setOrderId(addVO.getOrderId());
        orderBO.setOrderNo(addVO.getOrderNo());
        entity.setOrderBO(orderBO);
        repository.saveAndFlush(entity);

        return Wrapper.success();
    }

    /**
     * 分页查询应付订单结算
     *
     * @param sysUser:
     * @param pageVO:
     * @return 分页查询应付订单结算
     * @author ds
     * @date 2020/4/6
     **/
    @Override
    public Wrapper<PageData<MemberOrderSettlementVO>> pagePayableOrderSettlement(UserLoginCacheDTO sysUser, MemberOrderSettlementPageVO pageVO) {
        return pageOrderSettlement(sysUser, pageVO);
    }

    /**
     * 分页查询应收订单结算
     *
     * @param sysUser:
     * @param pageVO:
     * @return 分页查询应收订单结算
     * @author ds
     * @date 2020/4/6
     **/
    @Override
    public Wrapper<PageData<MemberOrderSettlementVO>> pageReceivableOrderSettlement(UserLoginCacheDTO sysUser, MemberOrderSettlementPageVO pageVO) {
        return pageOrderSettlement(sysUser, pageVO);
    }

    /**
     * 通联支付付款
     *
     * @param sysUser            登录用户
     * @param communicationPayVO 通联支付需要数据
     * @return 支付结果
     * @author 彭冬冬
     * @date 2021/11/30
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Wrapper<String> communicationPay(UserLoginCacheDTO sysUser, CommunicationPayVO communicationPayVO) {
        // step1：校验
        MemberSettlementDO entity = repository.findById(communicationPayVO.getId()).orElse(null);
        if (entity == null) {
            return Wrapper.fail(ResponseCode.SA_MEMBER_SETTLEMENT_NOT_EXIST);
        }
        if (!entity.getStatus().equals(SettlementStatusEnum.TO_BE_PAY.getCode())) {
            return Wrapper.fail(ResponseCode.SA_MEMBER_SETTLEMENT_NO_PAY);
        }
        // 生成在线支付的商户订单号
        String tradeNo = RandomStringUtils.random(7, "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890");

        // 2.组装【远端调用联通支付接口】请求数据
        EAccountOrderPayRequest request = new EAccountOrderPayRequest();
        request.setMemberId(entity.getMemberId()); // 会员id(收款方)
        request.setMemberRoleId(entity.getRoleId()); // 会员角色id(收款方)
        request.setPayMemberId(sysUser.getMemberId()); // 会员id(付款方)
        request.setPayMemberRoleId(sysUser.getMemberRoleId()); // 会员角色id(付款方)
        request.setOrderCode(tradeNo); // 商户订单号
        request.setPayMoney(entity.getAmount()); // 金额
        request.setServiceType(ServiceTypeEnum.SETTLEMENT_Pay.getCode()); // 服务类型: serviceTypeEnum类
        // request.setRemark(""); // 商品描述
        // 回调传递参数,存储操作对象
        String token = TokenUtil.instance().getToken();
/*        CallbackCommunicationPayVO callbackCommunicationPayVO = new CallbackCommunicationPayVO();
        callbackCommunicationPayVO.setTradeNo(tradeNo);
        callbackCommunicationPayVO.setToken(token);*/
        String json = JSONUtil.toJsonStr(sysUser);
        redisStringUtils.set(Constants.REDIS_KEY_SETTLEMENT_TOKEN_PREFIX + token, json, Constants.REDIS_SETTLEMENT_INDEX);
        request.setAttach(token); // 附加数据--在查询API和支付通知中原样返回
        request.setPayType(2); // 支付类型: 1-平台; 2-会员; (非必填，空值会根据资金归集模式)
        // 设置支付渠道
        request.setPayChannel(PayChannelTypeEnum.getAllInPayChannel(communicationPayVO.getPayChannelType())); // EAccountPayChannelEnum.class
        // request.setJsCode(""); // 小程序登录凭证code
        // request.setNonce(""); // 用于缓存订单参数的随机字符串
        redisStringUtils.set(Constants.SETTLEMENT_CODE_RESULT + entity.getSettlementNo(), entity.getStatus().toString(), 30 * 60L, Constants.REDIS_SETTLEMENT_INDEX);
        Wrapper<String> payWrapper = feignService.allInPayConsumeApply(request);
        // 返回结果失败，且不是重复支付
        if (payWrapper == null) {
            // 调用错误
            // 清除redis
            redisStringUtils.del(Constants.REDIS_KEY_SETTLEMENT_TOKEN_PREFIX + token, Constants.REDIS_SETTLEMENT_INDEX);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return Wrapper.fail(ResponseCode.SERVICE_PAY_ERROR);
        } else if (payWrapper.getCode() == ResponseCode.SUCCESS.getCode()) {
            // 调用成功
            // 保存支付的商户订单号
            entity.setTradeNo(tradeNo);
            repository.saveAndFlush(entity);
            // step3: 消息通知
            // 新增操作通知
            List<OperateNoticeVO> noticeVOList = new ArrayList<>();
            // 收款方待付款-1，待收款+1
            OperateNoticeVO noticeVO = new OperateNoticeVO();
            noticeVO.setMemberId(entity.getMemberId());
            noticeVO.setRoleId(entity.getRoleId());
            noticeVO.setDataSource(OperateDataSourceEnum.SETTLE_ACCOUNT_RECEIVABLE.getCode());
            noticeVO.setReduceOperateType(SettleAccountReceivableOperateTypeEnum.TO_BE_PAY.getCode());
            noticeVO.setReduceCount(1L);
            noticeVO.setIncreaseOperateType(SettleAccountReceivableOperateTypeEnum.TO_BE_RECEIPT.getCode());
            noticeVO.setIncreaseCount(1L);
            noticeVOList.add(noticeVO);
            feignService.addSystemOperateNoticeList(noticeVOList);

            // 新增系统通知
            SystemMessageRequest msgRequest = new SystemMessageRequest();
            msgRequest.setMemberId(entity.getMemberId());
            msgRequest.setRoleId(entity.getRoleId());
            msgRequest.setMessageNotice(MessageTemplateCode.receipt_settle_gathering);
            List<String> params = new ArrayList<>();
            params.add(entity.getSettlementNo());
            msgRequest.setParams(params);
            feignService.sendSystemMessage(msgRequest);
            // step5: 根据业务不同处理
            // 如果是请款单，则调用合同内部结果更新请款状态
            if (SettlementOrderTypeEnum.APPLY_AMOUNT.getCode().equals(entity.getOrderType())) {
                PayApplyAmountVO payApplyAmountVO = new PayApplyAmountVO();
                payApplyAmountVO.setApplyId(entity.getOrderBO().getOrderId());
                payApplyAmountVO.setUser(buildOperator(sysUser));
                controllerFeign.payApplyAmount(payApplyAmountVO);
                // 请款单完成付款
                SettlementOrderBO billData = entity.getOrderBO();
                applyAmountService.pay(billData.getOrderId());
            }
            return payWrapper;
        }
//        else if (payWrapper.getCode() == AllInPayErrorCodeEnum.DUPLICATE_ORDER_NUMBER.getCode()) {
//            // 重复支付-修改状态
//            callbackCommunicationPay(callbackCommunicationPayVO);
//            return payWrapper;
//        }
        else {
            // 错误回滚
            // 清除redis
            redisStringUtils.del(Constants.REDIS_KEY_SETTLEMENT_TOKEN_PREFIX + token, Constants.REDIS_SETTLEMENT_INDEX);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return payWrapper;
        }
    }

    /**
     * 应付账款管理 - 通联支付:付款方查看凭证
     *
     * @param sysUser 登录用户
     * @param id      结算单id
     * @return 通联支付:付款方查看凭证
     * @author 彭冬冬
     * @date 2021/11/30
     */
    @Override
    public Wrapper<CommunicationPayProveVO> getPayableCommunicationPayProve(UserLoginCacheDTO sysUser, Long id) {
        MemberSettlementDO entity = repository.findById(id).orElse(null);
        if (Objects.isNull(entity)) {
            throw new BusinessException(ResponseCode.SA_MEMBER_SETTLEMENT_NOT_EXIST);
        }
        CommunicationPayProveVO data = new CommunicationPayProveVO();
        data.setSettlementName(entity.getSettlementName());
        data.setAmount(entity.getAmount());
        data.setPayWayName(SettlementTypeEnum.getName(entity.getPayWay()));
        String settlementTime = Optional.ofNullable(entity.getPayTime())
                .filter(map -> map.compareTo(0L) > 0)
                .map(map -> DateUtil.format(new Date(map), DatePattern.NORM_DATETIME_PATTERN)).orElse("");
        data.setSettlementTime(settlementTime);
        data.setStatus(SettlementStatusEnum.getMessage(entity.getStatus()));
        return Wrapper.success(data);
    }


    /**
     * 应收账款管理 - 通联支付:付款方查看凭证
     *
     * @param sysUser 登录用户
     * @param id      结算单id
     * @return 通联支付:付款方查看凭证
     * @author 彭冬冬
     * @date 2021/11/30
     */
    @Override
    public Wrapper<CommunicationPayProveVO> getReceivableCommunicationPayProve(UserLoginCacheDTO sysUser, Long id) {
        return getPayableCommunicationPayProve(sysUser, id);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Wrapper<Boolean> callbackCommunicationPay(CallbackCommunicationPayVO callbackCommunicationPayVO) {
        // 1、校验
        String tradeNo = callbackCommunicationPayVO.getTradeNo();
        MemberSettlementDO entity = repository.findByTradeNo(tradeNo).orElse(null);
        // 获取token,存储炒作用户
        Boolean exists = redisStringUtils.exists(Constants.REDIS_KEY_SETTLEMENT_TOKEN_PREFIX + callbackCommunicationPayVO.getToken(), Constants.REDIS_SETTLEMENT_INDEX);
        if (!exists) {
            return Wrapper.fail(ResponseCode.TOKEN_DOES_NOT_EXIST_IN_CACHE);
        }
        String json = redisStringUtils.get(Constants.REDIS_KEY_SETTLEMENT_TOKEN_PREFIX + callbackCommunicationPayVO.getToken(), Constants.REDIS_SETTLEMENT_INDEX);
        UserLoginCacheDTO sysUser = JSONUtil.toBean(json, UserLoginCacheDTO.class);
        if (Objects.isNull(sysUser)) {
            return Wrapper.fail(ResponseCode.TOKEN_DOES_NOT_EXIST_IN_CACHE);
        }
        if (entity == null) {
            return Wrapper.fail(ResponseCode.SA_MEMBER_SETTLEMENT_NOT_EXIST.getCode());
        }
        if (entity.getStatus().equals(SettlementStatusEnum.COMPLETE.getCode())) {
            // 修改前端轮询结果
            redisStringUtils.set(Constants.SETTLEMENT_CODE + entity.getSettlementNo(), entity.getStatus().toString(), 30 * 60L, Constants.REDIS_SETTLEMENT_INDEX);
            return Wrapper.success(true);
        }
        // 待收款
        if (!(entity.getStatus().equals(SettlementStatusEnum.TO_BE_RECEIPT.getCode())
                || entity.getStatus().equals(SettlementStatusEnum.TO_BE_PAY.getCode()))) {
            return Wrapper.fail(ResponseCode.SA_MEMBER_SETTLEMENT_NO_PAY.getCode());
        }
        // 5.正常调用修改结算单完成
        Long now1 = System.currentTimeMillis();
        entity.setStatus(SettlementStatusEnum.COMPLETE.getCode());
        entity.setUpdateTime(now1);
        entity.setPayTime(now1);
        repository.saveAndFlush(entity);

        // 新增操作通知
        List<OperateNoticeVO> noticeVOList = new ArrayList<>();
        // 支付方待付款-1，待收款+1
        OperateNoticeVO noticeVO = new OperateNoticeVO();
        noticeVO.setMemberId(entity.getPayMemberId());
        noticeVO.setRoleId(entity.getPayRoleId());
        noticeVO.setDataSource(OperateDataSourceEnum.SETTLE_ACCOUNT_PAYABLE.getCode());
        noticeVO.setReduceOperateType(SettleAccountPayableOperateTypeEnum.TO_BE_PAY.getCode());
        noticeVO.setReduceCount(1L);
        noticeVO.setIncreaseOperateType(SettleAccountPayableOperateTypeEnum.TO_BE_RECEIPT.getCode());
        noticeVO.setIncreaseCount(1L);
        noticeVOList.add(noticeVO);
        // 收款方待付款-1，待收款+1
        noticeVO = new OperateNoticeVO();
        noticeVO.setMemberId(entity.getMemberId());
        noticeVO.setRoleId(entity.getRoleId());
        noticeVO.setDataSource(OperateDataSourceEnum.SETTLE_ACCOUNT_RECEIVABLE.getCode());
        noticeVO.setReduceOperateType(SettleAccountReceivableOperateTypeEnum.TO_BE_PAY.getCode());
        noticeVO.setReduceCount(1L);
        noticeVO.setIncreaseOperateType(SettleAccountReceivableOperateTypeEnum.TO_BE_RECEIPT.getCode());
        noticeVO.setIncreaseCount(1L);
        noticeVOList.add(noticeVO);
        feignService.addSystemOperateNoticeList(noticeVOList);

        // 新增系统通知
        SystemMessageRequest messageRequest = new SystemMessageRequest();
        messageRequest.setMemberId(entity.getMemberId());
        messageRequest.setRoleId(entity.getRoleId());
        messageRequest.setMessageNotice(MessageTemplateCode.receipt_settle_gathering);
        List<String> params = new ArrayList<>();
        params.add(entity.getSettlementNo());
        messageRequest.setParams(params);
        feignService.sendSystemMessage(messageRequest);


        // 如果是请款单，则调用合同内部结果更新请款状态
        if (SettlementOrderTypeEnum.APPLY_AMOUNT.getCode().equals(entity.getOrderType())) {
            PayApplyAmountVO payApplyAmountVO = new PayApplyAmountVO();
            payApplyAmountVO.setApplyId(entity.getOrderBO().getOrderId());
            payApplyAmountVO.setUser(buildOperator(sysUser));
            controllerFeign.payApplyAmount(payApplyAmountVO);
        }
        redisStringUtils.del(Constants.REDIS_KEY_SETTLEMENT_TOKEN_PREFIX + callbackCommunicationPayVO.getToken(), Constants.REDIS_SETTLEMENT_INDEX);
        // 修改前端轮询结果
        redisStringUtils.set(Constants.SETTLEMENT_CODE_RESULT + entity.getSettlementNo(), entity.getStatus().toString(), 30 * 60L, Constants.REDIS_SETTLEMENT_INDEX);
        return Wrapper.success(true);
    }

    @Override
    public Wrapper<Boolean> getAllInPayResult(UserLoginCacheDTO sysUser, String settlementNo) {
        if (!redisStringUtils.exists(Constants.SETTLEMENT_CODE_RESULT + settlementNo, Constants.REDIS_SETTLEMENT_INDEX)) {
            return Wrapper.success(false);
        }
        String res = redisStringUtils.get(Constants.SETTLEMENT_CODE_RESULT + settlementNo, Constants.REDIS_SETTLEMENT_INDEX);
        boolean result = res.equals(SettlementStatusEnum.COMPLETE.getCode().toString());
        return Wrapper.success(result);
    }

    @Override
    public Wrapper<String> getAllInPayTradeNo(UserLoginCacheDTO sysUser, Long id) {
        MemberSettlementDO entity = repository.findById(id).orElse(null);
        if (Objects.isNull(entity)) {
            throw new BusinessException(ResponseCode.SA_MEMBER_SETTLEMENT_NOT_EXIST);
        }
        if (entity.getStatus().equals(SettlementStatusEnum.COMPLETE.getCode())) {
            throw new BusinessException(ResponseCode.ACCOUNTS_PAYABLE_SETTLEMENT_ALL_IN_ALREADY_PAID);
        }
        String data = entity.getTradeNo();
        return Wrapper.success(data);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Wrapper<Void> save(MemberSettlementBaseVO saveVO) {
        if (saveVO == null) {
           return Wrapper.fail(ResponseCode.SA_BUSINESS_APPLY_AMOUNT_NOT_EXIST);
        }
        // step1: 构建保存实体
        MemberSettlementDO entity = new MemberSettlementDO();
        // 结算单据信息
        SettlementOrderBO orderBO = Optional.ofNullable(saveVO.getOrderVO()).map(map -> {
            SettlementOrderBO data = new SettlementOrderBO();
            data.setOrderId(map.getOrderId());
            data.setOrderNo(map.getOrderNo());
            return data;
        }).orElse(null);

        entity.setOrderBO(orderBO);
        entity.setStartTime(saveVO.getStartTime()); // 开始时间
        entity.setEndTime(saveVO.getEndTime()); // 结束时间
        entity.setSettlementName(saveVO.getSettlementName()); // 结算方
        entity.setPayName(saveVO.getPayName()); // 支付方
        entity.setStrategyId(saveVO.getStrategyId()); // 策略id
        entity.setMemberId(saveVO.getMemberId()); // 结算会员id
        entity.setRoleId(saveVO.getRoleId()); // 结算角色id
        entity.setPayMemberId(saveVO.getPayMemberId()); // 付款会员id
        entity.setPayRoleId(saveVO.getPayRoleId()); // 付款角色id
        // 结算单号
        String settlementNo = SerialNumberUtils.getNumber(redisStringUtils, Constants.SETTLEMENT_CODE, Constants.SETTLEMENT_CODE_DATE, Constants.SETTLEMENT_CODE_NUM_LEN);
        entity.setSettlementNo(settlementNo);
        entity.setSettlementWay(saveVO.getSettlementWay()); // 结算方式
        entity.setOrderType(saveVO.getOrderType()); // 结算单据类型
        entity.setTotalCount(saveVO.getTotalCount()); // 总单数
        entity.setAmount(saveVO.getAmount()); // 结算金额
        entity.setSettlementTime(saveVO.getSettlementTime()); // 结算时间
        entity.setStatus(saveVO.getStatus()); // 结算状态
        entity.setPayWay(saveVO.getPayWay()); // 支付方式
        entity.setPrePayTime(saveVO.getPrePayTime()); // 预付款时间
//        entity.setPayTime(saveVO.getPrePayTime()); // 实际付款时间
        entity.setSettlementDate(saveVO.getSettlementDate()); // 结算日期
        entity.setStrategyId(saveVO.getStrategyId()); // 结算策略id
        entity.setUpdateTime(System.currentTimeMillis()); // 更新时间
        repository.saveAndFlush(entity);
        // step2: 新增系统消息通知
        SystemMessageRequest request = new SystemMessageRequest();
        request.setMemberId(entity.getMemberId());
        request.setRoleId(entity.getRoleId());
        request.setMessageNotice(MessageTemplateCode.receipt_settle_check);
        List<String> params = new ArrayList<>();
        params.add(entity.getSettlementNo());
        request.setParams(params);
        feignService.sendSystemMessage(request);
        return Wrapper.success();
    }

    private Wrapper<PageData<MemberOrderSettlementVO>> pageOrderSettlement(UserLoginCacheDTO sysUser, MemberOrderSettlementPageVO pageVO) {
        // step 1: 组装查询条件
        Specification<MemberOrderSettlementDO> spec = (root, query, criteriaBuilder) -> {
            List<Predicate> list = new ArrayList<>();
            list.add(criteriaBuilder.equal(root.get("settlementId").as(Long.class), pageVO.getSettlementId()));

            if (StringUtils.hasLength(pageVO.getStartTime())) {
                list.add(criteriaBuilder.greaterThanOrEqualTo(root.get("orderTime").as(Long.class), DateUtil.parse(pageVO.getStartTime()).getTime()));
            }
            if (StringUtils.hasLength(pageVO.getEndTime())) {
                list.add(criteriaBuilder.lessThanOrEqualTo(root.get("orderTime").as(Long.class), DateUtil.parse(pageVO.getEndTime()).getTime()));
            }
            if (StringUtils.hasLength(pageVO.getPayStartTime())) {
                list.add(criteriaBuilder.greaterThanOrEqualTo(root.get("payTime").as(Long.class), DateUtil.parse(pageVO.getPayStartTime()).getTime()));
            }
            if (StringUtils.hasLength(pageVO.getPayEndTime())) {
                list.add(criteriaBuilder.lessThanOrEqualTo(root.get("payTime").as(Long.class), DateUtil.parse(pageVO.getPayEndTime()).getTime()));
            }
            if (StringUtils.hasLength(pageVO.getOrderNo())) {
                list.add(criteriaBuilder.like(root.get("orderNo").as(String.class), "%" + pageVO.getOrderNo().trim() + "%"));
            }
            if (StringUtils.hasLength(pageVO.getOrderAbstract())) {
                list.add(criteriaBuilder.like(root.get("orderAbstract").as(String.class), "%" + pageVO.getOrderAbstract().trim() + "%"));
            }
            Predicate[] p = new Predicate[list.size()];
            return criteriaBuilder.and(list.toArray(p));
        };

        // step 2: 组装分页参数
        Pageable page = PageRequest.of(pageVO.getCurrent() - 1, pageVO.getPageSize(), Sort.by("orderTime").ascending());

        // step 3: 分页查询数据
        Page<MemberOrderSettlementDO> result = orderRepository.findAll(spec, page);

        // step 4: 组装返回数据
        List<MemberOrderSettlementVO> resultList = result.getContent().stream().map(r -> {
            MemberOrderSettlementVO queryVO = new MemberOrderSettlementVO();
            queryVO.setOrderNo(r.getOrderNo());
            queryVO.setOrderAbstract(r.getOrderAbstract());
            queryVO.setOrderTypeName(SettlementMemberOrderTypeEnum.getMessage(r.getOrderType()));
            queryVO.setTypeName(ReceiptInvoiceOrderTypeEnum.getMessage(r.getType()));
            queryVO.setOrderTime(DateUtil.format(DateUtil.date(r.getOrderTime()), "yyyy-MM-dd HH:mm"));
            queryVO.setOrderAmount(BigDecimal.valueOf(r.getOrderAmount()).setScale(2, RoundingMode.HALF_UP).doubleValue());
            queryVO.setPayCount(r.getPayCount());
            queryVO.setPayTime(DateUtil.format(DateUtil.date(r.getPayTime()), "yyyy-MM-dd HH:mm"));
            queryVO.setPayAmount(BigDecimal.valueOf(r.getPayAmount()).setScale(2, RoundingMode.HALF_UP).doubleValue());
            queryVO.setSettlementAmount(BigDecimal.valueOf(r.getSettlementAmount()).setScale(2, RoundingMode.HALF_UP).doubleValue());
            queryVO.setIsHasTax(r.getIsHasTax());
            queryVO.setIsHasTaxName(CommonBooleanEnum.getMessage(r.getIsHasTax()));
            queryVO.setTaxRate(r.getTaxRate());
            return queryVO;
        }).collect(Collectors.toList());

        return Wrapper.success(new PageData<>(result.getTotalElements(), resultList));
    }

//    /**
//     * 校验数据权限
//     *
//     * @param roleType     1：供应商查询，2：采购商查询
//     * @param settlementId 结算id
//     * @param memeberId    会员id
//     * @param roleId       角色id
//     */
//    private void checkOrderSettlementDataLimit(Integer roleType, Long settlementId, Long memeberId, Long roleId) {
//        MemberSettlementDO memberSettlement = repository.findById(settlementId).orElse(null);
//        if (memberSettlement == null) {
//            throw new BusinessException(ResponseCode.SA_MEMBER_SETTLEMENT_NOT_EXIST);
//        }
//        if (roleType == 1) {//应收款
//            if (!memberSettlement.getMemberId().equals(memeberId) || memberSettlement.getRoleId().equals(roleId)) {
//                throw new BusinessException(ResponseCode.SA_MEMBER_SETTLEMENT_NOT_DATA_LIMIT);
//            }
//        } else {
//            if (!memberSettlement.getPayMemberId().equals(memeberId) || memberSettlement.getPayRoleId().equals(roleId)) {
//                throw new BusinessException(ResponseCode.SA_MEMBER_SETTLEMENT_NOT_DATA_LIMIT);
//            }
//        }
//    }
}
